home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The PC-SIG Library 10
/
The PC-Sig Library - Shareware for the IBM PC and Compatibles (PC-SIG)(Tenth Edition Disks 1-2804)(1991).iso
/
PC_SIGCD
/
05
/
6
/
DISK0564.ZIP
/
SOURCE.ARC
/
MODEM.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-07-09
|
22KB
|
877 lines
/* modem program for MSDOS 2, assumes IBM compatibility at bios level */
/* for Aztec C86 v. 3.40a */
/* by Jon Dart, 3012 Hawthorn St., San Diego, CA 92104 */
/* to make modem.exe:
cc modem
ln modem -lb -lm -lc */
/* log:
Version 1.7 (25-Jan-87): more minor changes to XMODEM
Version 1.6 (11-Jan-87): minor cosmetic changes
Version 1.5 (30-Oct-86): improved XMODEM receive logic
Version 1.4 (14-Oct-86): SET no longer sets echo=on.
Version 1.3 (26-Jun-86): fixes bug in DIR routine
Version 1.26 (Apr 1986): XMODEM works
*/
#define Version "1.7 (25-Jan-87)"
#include <stdio.h>
#include <dioctl.h>
#include <time.h>
#include <fcntl.h>
#include <ctype.h>
#include "truth.h"
#include "ascii.h"
#include "fixpath.h"
#define skipsp(s,c) while (((c = *s) == SPACE) || (c==TAB)) s++
#define chkabort(c) (keyin(&c) && (c==CAN))
#define WAITTIME 20
#define TIME1 10 /* max time to wait for char. */
#define MAXLINE 80
#define MAXFILES 512 /* max number of files displayed by directory routine */
#define NAMESIZE 13 /* size of a DOS file name (+1 for EOS) */
#define PATHSIZE 65 /* size of a DOS2 path name (+1 for EOS) */
extern char *malloc();
extern char *calloc();
extern long lseek();
struct optstruc {
int echo; /* <> 0 for echo */
int baud; /* default baud rate */
int db; /* default data bits */
int sb; /* default stop bits */
char parity; /* default parity */
int checksum; /* checksum mode */
} options = {FALSE,1200,8,1,'N',FALSE};
int sendcksum; /* TRUE if checksum mode requested on send, FALSE if CRC */
unsigned char *buf;
struct sgttyb stty;
waitch(c,t)
/* waits for char. from host. If waiting time (t) exceeded, returns
function value FALSE */
register char *c; int t;
{
register int i; char cin; long oldtime, newtime;
int oldsec,newsec;
i = 0; time(&oldtime);
oldsec = oldtime & 0x0f;
while (!receive(c)) {
if (i++>255) {
time(&newtime);
newsec = newtime & 0x0f;
if (newsec!=oldsec) {
t -= 2;
if (t<=0)
return(FALSE);
else
oldsec = newsec;
}
}
}
/* got something */
/* putcb(*c); */
return(TRUE);
}
show(c)
char c;
{
if ((c<32) && (c!=LF) && (c!=CR)) {
putchar('^'); putchar(c+'A'-1);
}
else if (c==LF) putchar('\n');
else putchar(c);
}
showmode()
/* show currently enabled protocol */
{
if (options.checksum)
printf("CHECKSUM");
else
printf("CRC");
}
crc(buf)
unsigned char *buf;
/* compute a crc for the buffer */
{
#define swap(x) ((x*256)+(x/256))
#define lo(x) (x & 0xFF)
register unsigned int aa;
register int i;
/* compute the crc. (Ref: Dr. Dobb's Journal, Feb. 1986, p. 80) */
aa = 0;
for (i=0;i<128;i++) {
aa = swap(aa) ^ buf[i];
aa = aa ^ (lo(aa) >> 4);
aa = aa ^ (swap(lo(aa)) << 4) ^ (lo(aa) << 5);
}
return(aa);
}
cksum(buf)
unsigned char *buf;
/* compute checksum for buffer */
{
unsigned int sum,i;
sum = 0;
for (i=0;i<128;i++) {
sum = (sum + (*buf++ & 0377)) & 0377;
}
return(sum);
}
char waitSOH(sec)
int sec;
/* waits for SOH from remote, returns SOH or CAN if timeout or cancelled */
{
int errcnt, wtime, count, t, timeout, cans;
char c;
t = 0; errcnt = 0;
for (;;) {
wtime = 10; /* 10 sec to wait for 1st char */
cans = 0; /* counts cancel chars. recvd. */
count = 160; /* max. chars to gobble before SOH */
if (chkabort(c)) return(CAN);
if (sec==1) transmit((options.checksum) ? NAK : 'C');
do {
if ((timeout = !waitch(&c,wtime))==0) {
/* got a char */
if (c==EOT) {
if (!waitch(&c,5)) /* make sure no more chars. */
/* this reduces chance EOT is noise */
return(EOT);
}
if (c==SOH)
return(c);
else if (c==CAN) {
if (++cans >= 5) return(c);
}
else if (wtime>1) { /* got some garbage */
printf(" ++ Received %02x not SOH ++\n",c);
wtime = 1; /* set timeout to 1 sec after 1st char recvd. */
}
else /* count chars recvd before SOH */
count--;
}
} while ((!timeout) && (count>0));
errcnt++;
if (timeout && (wtime>1)) { /* no char. received */
t++;
printf(" ++ TIMEOUT - %d ++\n",t);
}
if (errcnt>=10)
return(CAN);
else if (errcnt==5) { /* switch modes */
options.checksum = !options.checksum;
printf("*** Switching to ");
showmode();
printf(" mode ***\n");
}
}
}
get1sec(buf)
register unsigned char *buf;
/* get 1 sector from remote, return count (0 if timeout) */
{
register int i;
int max, t, wtime;
unsigned char c;
max = (options.checksum) ? 131 : 132;
wtime = 5;
for (i=0;i<max;i++) {
if (waitch(&c,wtime)) {
wtime = 2;
*buf++ = c;
}
else { /* timeout */
return(i);
}
if (i>129) wtime = 5; /* allow more time for checksum/crc */
}
return(i);
}
openfile(filename)
char *filename;
/* open file for receiving, return -1 if error, file descriptor if ok */
{
char sp[PATHSIZE],lip[PATHSIZE];
char c;
int fd;
if (fixpath(filename,sp,lip)==TYPE_UFN) { /* file exists */
printf("%s exists. Overwrite [Y or N]? ",filename);
while (!keyin(&c) && ((c = toupper(c)!='Y') && (c!='N'))) ;
if (c=='N')
return(-1);
}
if ((fd = open(filename,O_WRONLY + O_TRUNC))==-1) {
printf("Can't open: %s\n",filename);
return(-1);
}
else
return(fd);
}
goterror(sec,buf,count)
unsigned int sec; unsigned char *buf; unsigned int count;
/* checks receive buffer, returns TRUE if error */
{
int x,cx;
if ( ((sec & 0377) != buf[0])
|| ((sec & 0377) != ~buf[1]) ) {
printf(" ++ Error in sector number ++\n");
return(TRUE);
}
if (count < ((options.checksum) ? 131 : 132)) {
printf(" ++ ERROR - Short block ++\n");
return(TRUE);
}
if (options.checksum) {
x = cksum(buf+2);
cx = buf[130];
}
else {
x = crc(buf+2);
cx = (buf[130]*256) + (buf[131] & 0377);
}
if (cx==x)
return(FALSE);
else {
printf(" ++ ");
showmode();
printf(" Error: Received %04x Computed %04x",cx,x);
printf(" ++\n");
return(TRUE);
}
}
get(filename)
char *filename;
/* get file from remote, using XMODEM protocol */
{
char c;
int fd;
unsigned int sec, count, errcnt;
if ((fd = openfile(filename)) == -1)
return;
sec = 1;
putchar('\n');
showmode();
printf(" enabled.\nWaiting for first sector.\n");
errcnt = 0;
for (;;) {
c = waitSOH(sec);
if (c==EOT)
goto gotitall;
else if (c==CAN)
goto cancel;
/* got SOH */
printf("%cReceiving # %d ",CR,sec);
count = get1sec(buf);
if (goterror(sec,buf,count)) {
transmit(NAK);
errcnt++;
if (errcnt>10) goto cancel;
}
else {
if (write(fd,buf+2,128)!=128) {
printf(" ++ DISK WRITE ERROR ++\n");
goto cancel;
}
else {
transmit(ACK);
sec++;
}
}
}
close(fd);
return;
cancel:
printf("\n ++ RECEIVE FILE CANCELLED ++\n");
close(fd);
unlink(filename);
printf(" ++ UNFINISHED FILE DELETED ++\n");
return;
gotitall:
close(fd);
transmit(ACK);
printf("\nTransmission complete.\n");
return;
}
char waitready()
/* waits for CRC or Checksum request from remote */
/* returns 'C', NAK, or CAN */
{
char c;
int t;
t = 0;
while (t<10) {
if (chkabort(c))
return(CAN);
if (waitch(&c,10)) { /* got a char */
if (c==NAK) {
printf("Got checksum request.\n");
sendcksum = TRUE;
return(c);
}
else if (c=='C') {
printf("Got CRC request.\n");
sendcksum = FALSE;
return(c);
}
}
else { /* timeout */
t++;
printf(" ++ TIMEOUT - %d ++\n",t);
}
}
return(CAN);
}
send1sec(sec,buf)
int sec; register unsigned char *buf;
/* transmit 1 sector to remote */
{
register unsigned int i;
unsigned int x;
transmit(SOH);
transmit(sec & 0377);
transmit(~sec & 0377);
for (i=0;i<128;i++) {
transmit(buf[i]);
}
if (sendcksum) {
x = cksum(buf);
transmit(x & 0377);
}
else { /* CRC mode */
x = crc(buf);
transmit((x>>8) & 0377);
transmit(x & 0377);
}
}
char waitACK(sec)
int sec;
/* wait for ACK char from remote, after sending sec */
/* returns ACK (if sector OK), NAK (if not OK) or CAN (if too many errors
or transmission cancelled by sender */
{
int count,errcnt,wtime;
char c;
errcnt = 0; count = 160; wtime = 10; /* 10 secs. for 1st char */
do {
if (chkabort(c)) return(CAN);
c = CAN;
if (waitch(&c,wtime)) { /* got a char. */
wtime = 5;
if ((c==ACK) || (c==NAK))
return(c);
else { /* garbage char */
printf(" ++ %02x received not ACK ++\n",c);
--count;
}
}
else /* no char */
printf(" ++ TIMEOUT - no ACK - %d ++\n",++errcnt);
} while ((errcnt < 10) && (count>0));
return(CAN); /* too many errors */
}
send(filename)
char *filename;
/* send file to remote system, using XMODEM protocol */
{
int fd, sectors, sec, i, n, x, retry;
char c;
long filesize;
if ((fd = open(filename,O_RDONLY)) == -1) {
printf("\nCan't open: %s\n",filename);
return;
}
filesize = lseek(fd,0L,2);
sectors = filesize / 128;
if (filesize % 128) sectors++;
printf("File open to send. %d sectors, approx. %.1f minutes at %d baud.\n",
sectors,
( (1.0*sectors)/(1.0*options.baud))*29.4,
/* 29.4 is right for my system. It depends
somewhat on disk speed */
options.baud);
lseek(fd,0L,0);
printf("Waiting ready signal.\n");
if (waitready()==CAN)
goto cancel;
sec = 1;
waitch(&c,1); /* wait 1 sec */
while (( n = read(fd,buf,128)) > 0) {
if (n<128) { /* pad out last buffer */
buf[n] = '\032'; /* control-Z */
for (i=n+1;i<128;i++) buf[i] = '\0';
}
printf("%cSending # %d ",CR,sec);
send1sec(sec,buf);
retry = 0;
do {
if ((c=waitACK(sec))==ACK) {
sec++;
break;
}
else if (c==CAN)
goto cancel;
else if (c == NAK) {
printf(" ++ NAK received not ACK - %d ++\n",++retry);
if (retry<10) {
printf("%cResending # %d",CR,sec);
send1sec(sec,buf);
}
}
} while (retry<10);
if (retry>=10) goto cancel;
}
/* normal exit */
printf("\nTransmission complete.\n");
transmit(EOT);
if (!waitch(&c,10) || (c!=ACK)) /* warn user about lack of ack */
printf("++ WARNING: Final ACK not received ++\n");
close(fd);
return;
cancel:
printf("\n ++ SEND FILE CANCELLED. ++\n");
transmit(CAN);
close(fd);
return;
}
toggle_echo()
{
options.echo = !options.echo;
if (options.echo)
printf("\nEcho is ON\n");
else
printf("\nEcho is OFF\n");
}
toggle_prot()
/* change protocol (CRC or Checksum) */
{
options.checksum = !options.checksum;
printf("\nMode is ");
showmode();
putchar('\n');
}
show_options()
{
printf("\nCurrent option settings:\n\n");
printf("Baud rate = %d\n",options.baud);
printf("Parity = %c\n",options.parity);
printf("Data bits = %d\n",options.db);
printf("Stop bits = %d\n",options.sb);
printf("Echo = %s\n",(options.echo) ? "ON" : "OFF");
printf("Protocol = "); showmode(); putchar('\n');
}
menu()
/* show available commands */
{
printf("\nM = Show this list DEL = Delete file\n");
printf("? = Show current options DIR = Show directory\n");
printf("C = Toggle protocol (CRC/Checksum) SET = Set communication params\n");
printf("E = Toggle echo\n");
printf("R = Receive File\n");
printf("S = Send File\n");
printf("T = Enter Terminal Mode\n");
printf("X = Exit to DOS\n");
}
terminal()
/* terminal mode */
{
char c;
for (;;) {
if (keyin(&c)) {
if (c==CTRLE)
break;
if (options.echo) putchar((c==CR) ? '\n' : c);
transmit(c);
}
if (receive(&c))
putchar(c);
}
}
cmp(ptra,ptrb)
char **ptra, **ptrb;
/* compare routine for qsort */
{
return(strcmp(*ptra,*ptrb));
}
show1file(filename)
char *filename;
{
int i;
char c;
for (i=0;i<8;i++) {
if ( ((c = *filename)!='\0') && (c!='.') ) {
putchar(c);
filename++;
}
else
putchar(SPACE);
}
putchar('.');
if (*filename == '.')
filename++;
for (i=0;i<3;i++) {
if ((c = *filename)!='\0') {
putchar(c);
filename++;
}
else
putchar(SPACE);
}
}
showfiles(dirptrs,count)
char *dirptrs[]; int count;
/* display directory */
{
int i,n;
n = 0;
for (i=0;i<count;i++) {
show1file(dirptrs[i]);
if (++n >= 5) {
putchar(NL);
n = 0;
}
else
printf(" | ");
}
}
dodel(filespec)
char *filespec;
{
int type;
char sp[PATHSIZE],lip[PATHSIZE];
char c;
skipsp(filespec,c);
if (c=='\0') {
printf("\nFile name required.\n");
return;
}
type = fixpath(filespec,sp,lip);
if (type == TYPE_UNK) {
printf("\nCan't find: %s\n",filespec);
return;
}
else if (type == TYPE_AFN) {
printf("\nWild cards not allowed.\n");
return;
}
else if ((type == TYPE_DIR) || (type==TYPE_DSP)) {
printf("\nCan't delete a directory.\n");
return;
}
else if (type == TYPE_DRV) {
printf("\nFile name required.\n");
return;
}
else {
if (unlink(filespec))
printf("\nCan't delete: %s\n",filespec);
}
}
dodir(filespec)
char *filespec;
/* display a sorted disk directory */
{
char c;
char filename[PATHSIZE], sp[PATHSIZE],lip[PATHSIZE];
char *dirptrs[MAXFILES];
char *dirbuf;
int attrib, count, n;
unsigned long freesp;
struct regstruc {
unsigned int ax,bx,cx,dx,si,di;
} inregs,outregs;
if ((dirbuf = malloc(MAXFILES*NAMESIZE)) == NULL) {
printf("\nCan't allocate buffer space.\n");
return;
}
skipsp(filespec,c);
fixpath(filespec,sp,lip);
count = 0;
if (getfirst(sp,0x21,filename,&attrib)) {
do {
n = 13;
dirptrs[count] = dirbuf+(count*NAMESIZE);
cpycnt(filename,dirptrs[count],&n);
count++;
} while (getnext(filename,&attrib) && (count<MAXFILES-1));
qsort(dirptrs,count,2,cmp);
showfiles(dirptrs,count);
inregs.dx = (*(sp+1)==':') ? (int) (*sp-'A'+1) : 0;
doscall(0x36,&inregs,&outregs);
freesp = ((long) outregs.bx)*((long) outregs.cx)*((long) outregs.ax)/1024L;
if (count % 5) putchar(NL);
printf("(%ldK bytes free)\n",freesp);
}
else
printf("\nNo matching files.\n");
free(dirbuf);
}
cmdsrc(cp)
char *cp;
/* see if the string pointed at by cp matches a valid 3-letter command.
If so, return the command number. If no match, return 0. */
{
static char cmdlist[] = "DELDIRSET";
char *cpp, *lp;
int n,j;
n = 1; lp = cmdlist;
while (*lp) {
cpp = cp;
for (j=0;j<3;j++)
if (*(cpp+j) != *(lp+j)) break;
if (j==3) /* got a match */
return(n);
else { /* try next 3 letters in cmdlist */
n++;
lp += 3;
}
}
return(0);
}
char *getword(line,word,n)
register char *line,*word; int n;
{
while ((!isspace(*line)) && (--n > 0))
*word++ = *line++;
*word = '\0';
return(line);
}
set_error()
{
printf("\nForm of SET command:\n");
printf(" SET Baud Parity DataBits StopBits\n");
printf("e.g.: SET 300 N 8 1\n");
}
doset(cmdline)
char *cmdline;
/* handle SET command */
{
struct optstruc newopts;
char c;
char s[10];
char *lp;
newopts = options; /*bug fix, 10-14-86 */
skipsp(cmdline,c);
if (c=='\0') {
set_error();
return;
}
lp = cmdline;
lp = getword(lp,s,10);
if (sscanf(s,"%d",&newopts.baud)!=1) {
printf("\nError in baud rate.\n");
set_error();
return;
}
skipsp(lp,c);
if (*lp)
newopts.parity = *lp++;
else
newopts.parity = options.parity;
skipsp(lp,c);
lp = getword(lp,s,10);
if (sscanf(s,"%d",&newopts.db)!=1)
newopts.db = options.db;
skipsp(lp,c);
lp = getword(lp,s,10);
if (sscanf(s,"%d",&newopts.sb)!=1)
newopts.sb = options.sb;
if (cominit(newopts.baud,newopts.parity,newopts.sb,newopts.db)==-1) {
printf("\nError in parameters.\n");
set_error();
cominit(options.baud,options.parity,options.sb,options.db);
return;
}
else
options = newopts;
}
command(term,quit)
int *term, *quit;
/* get command line from console and execute command */
{
unsigned char c, cmd;
unsigned char *cp;
unsigned char cmdline[128];
gets(cmdline);
cp = cmdline;
/* make line upper-case */
while (*cp) { *cp = toupper(*cp); cp++; }
cp = cmdline;
skipsp(cp,cmd);
if (cmd=='\0')
return;
c = *(cp+1);
if (isspace(c) || (c=='\0')) { /* single-letter command */
cp++;
skipsp(cp,c); /* make cp point to 1st non-blank char. after command */
switch(cmd) {
case 'M': menu(); break;
case '?': show_options(); break;
case 'X': *quit = TRUE; break;
case 'T': *term = TRUE;
printf("%s","\nEntering terminal mode. Type CTRL-E to exit.\n\n");
break;
case 'R': get(cp); break;
case 'S': send(cp); break;
case 'E': toggle_echo(); break;
case 'C': toggle_prot(); break;
default:
putchar(BEL);
}
}
else { /* possible multi-letter command */
switch (cmdsrc(cp)) {
case 0: putchar(BEL); break;
case 1: dodel(cp+3); break;
case 2: dodir(cp+3); break;
case 3: doset(cp+3); break;
}
}
}
main()
{
int stat, ctlcstat, term, quit;
struct regstruc {
unsigned int ax,bx,cx,dx,si,di;
} inregs, outregs;
char *cmdline;
clrscr();
printf("Modem Version %s ... by Jon Dart\n\n",Version);
stat = cominit(options.baud,options.parity,options.sb,options.db);
if ((stat & 0x10)==0)
printf("\n*** CTS not set ***\n");
if ((stat & 0x20)==0)
printf("\n*** DTR not set ***\n");
printf("\nType M for menu.\n");
cmdline = malloc(128);
buf = (unsigned char *) malloc(135);
if ((buf==NULL) || (cmdline==NULL)) {
printf("Out of memory.");
exit(1);
}
inregs.ax = 0;
doscall(0x33,&inregs,&outregs); /* get current ^C status */
ctlcstat = outregs.dx; /* save it */
inregs.ax = 1;
inregs.dx = 0;
doscall(0x33,&inregs,&outregs); /* turn off ^C trapping */
term = FALSE; quit = FALSE;
while (!quit) {
printf("\nCOMMAND>> ");
command(&term,&quit);
if (term) {
terminal();
term = FALSE;
}
}
free(cmdline);
free(buf);
inregs.ax = 1;
inregs.dx = ctlcstat;
doscall(0x33,&inregs,&outregs); /* restore old ^C status */
}